package net.xdclass.service.stress.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.dto.ReportDTO;
import net.xdclass.dto.stress.StressCaseDTO;
import net.xdclass.enums.BizCodeEnum;
import net.xdclass.enums.ReportStateEnum;
import net.xdclass.enums.StressSourceTypeEnum;
import net.xdclass.enums.TestTypeEnum;
import net.xdclass.exception.BizException;
import net.xdclass.feign.ReportFeignService;
import net.xdclass.mapper.EnvironmentMapper;
import net.xdclass.mapper.StressCaseMapper;
import net.xdclass.model.EnvironmentDO;
import net.xdclass.model.StressCaseDO;
import net.xdclass.req.ReportSaveReq;
import net.xdclass.req.stress.StressCaseSaveReq;
import net.xdclass.req.stress.StressCaseUpdateReq;
import net.xdclass.service.stress.StressCaseService;
import net.xdclass.service.stress.core.StressJmxEngine;
import net.xdclass.service.stress.core.StressSimpleEngine;
import net.xdclass.util.JsonData;
import net.xdclass.util.SpringBeanUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * @author 小二
 */
@Service
@Slf4j
public class StressCaseServiceImpl implements StressCaseService {

    @Resource
    private StressCaseMapper stressCaseMapper;

    @Resource
    private ReportFeignService reportFeignService;

    @Resource
    private ApplicationContext applicationContext;

    @Resource
    private EnvironmentMapper environmentMapper;

    /**
     * 根据项目ID和用例ID查找压力测试用例
     *
     * @param projectId 项目ID
     * @param caseId 用例ID
     * @return 返回找到的StressCaseDTO对象，如果找不到则返回null
     */
    @Override
    public StressCaseDTO findById(Long projectId, Long caseId) {
        LambdaQueryWrapper<StressCaseDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StressCaseDO::getProjectId, projectId)
                .eq(StressCaseDO::getId, caseId);
        StressCaseDO stressCaseDO = stressCaseMapper.selectOne(queryWrapper);
        return SpringBeanUtil.copyProperties(stressCaseDO, StressCaseDTO.class);
    }

    /**
     * 删除指定项目ID和ID的压力测试用例
     *
     * @param projectId 项目ID
     * @param id 用例ID
     * @return 删除操作的成功与否，成功返回受影响的行数
     */
    @Override
    public int delete(Long projectId, Long id) {
        LambdaQueryWrapper<StressCaseDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StressCaseDO::getProjectId, projectId)
                .eq(StressCaseDO::getId, id);
        return stressCaseMapper.delete(queryWrapper);
    }

    /**
     * 保存压力测试用例
     *
     * @param req 压力测试用例的保存请求对象
     * @return 插入操作的成功与否，成功返回受影响的行数
     */
    @Override
    public int save(StressCaseSaveReq req) {
        StressCaseDO stressCaseDO = SpringBeanUtil.copyProperties(req, StressCaseDO.class);
        return stressCaseMapper.insert(stressCaseDO);
    }

    /**
     * 更新压力测试用例
     *
     * @param req 压力测试用例的更新请求对象
     * @return 更新操作的成功与否，成功返回受影响的行数
     */
    @Override
    public int update(StressCaseUpdateReq req) {
        StressCaseDO stressCaseDO = SpringBeanUtil.copyProperties(req, StressCaseDO.class);
        LambdaQueryWrapper<StressCaseDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StressCaseDO::getProjectId, stressCaseDO.getProjectId())
                .eq(StressCaseDO::getId, stressCaseDO.getId());
        return stressCaseMapper.update(stressCaseDO, queryWrapper);
    }

    /**
     * 执行压力测试用例
     * 【1】查询用例详情
     * 【2】初始化测试报告
     * 【3】判断压测类型 JMX、SIMPLE
     * 【4】初始化测试引擎
     * 【5】组装测试计划
     * 【6】执行压测
     * 【7】发送压测结果明细
     * 【8】压测完成清理数据
     * 【9】通知压测结束
     */
    @Async("testExecutor")
    @Override
    public void execute(Long projectId, Long caseId) {
        LambdaQueryWrapper<StressCaseDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StressCaseDO::getProjectId, projectId)
                .eq(StressCaseDO::getId, caseId);

        StressCaseDO stressCaseDO = stressCaseMapper.selectOne(queryWrapper);

        if (Objects.isNull(stressCaseDO)) {
            return ;
        }
        // 初始化测试报告
        ReportSaveReq reportSaveReq = ReportSaveReq.builder().projectId(stressCaseDO.getProjectId())
                .caseId(stressCaseDO.getId())
                .startTime(System.currentTimeMillis())
                .executeState(ReportStateEnum.EXECUTING.name())
                .name(stressCaseDO.getName())
                .type(TestTypeEnum.STRESS.name())
                .build();
        JsonData jsonData = reportFeignService.save(reportSaveReq);
        if (jsonData.isSuccess()) {
            ReportDTO reportDTO = jsonData.getData(ReportDTO.class);
            if (Objects.equals(stressCaseDO.getStressSourceType(), StressSourceTypeEnum.JMX.name())) {
                runJmxStressCase(stressCaseDO, reportDTO);
            } else if(Objects.equals(stressCaseDO.getStressSourceType(), StressSourceTypeEnum.SIMPLE.name())) {
                runSimpleStressCase(stressCaseDO, reportDTO);
            } else {
                throw new BizException(BizCodeEnum.STRESS_UNSUPPORTED);
            }
        }
    }

    /**
     * 运行基于JMX的压力测试用例
     *
     * @param stressCaseDO 压力测试用例对象
     * @param reportDTO 测试报告对象
     */
    private void runJmxStressCase(StressCaseDO stressCaseDO, ReportDTO reportDTO) {
        StressJmxEngine jmxEngine = new StressJmxEngine(stressCaseDO, reportDTO, applicationContext);

        jmxEngine.startStressTest();
    }

    /**
     * 运行基于SIMPLE的压力测试用例
     *
     * @param stressCaseDO 压力测试用例对象
     * @param reportDTO 测试报告对象
     */
    private void runSimpleStressCase(StressCaseDO stressCaseDO, ReportDTO reportDTO) {
        EnvironmentDO environmentDO = environmentMapper.selectById(stressCaseDO.getEnvironmentId());

        StressSimpleEngine stressSimpleEngine = new StressSimpleEngine(environmentDO, stressCaseDO, reportDTO, applicationContext);

        stressSimpleEngine.startStressTest();
    }
}
